home *** CD-ROM | disk | FTP | other *** search
/ Cream of the Crop 1 / Cream of the Crop 1.iso / PROGRAM / UUPC11QS.ARJ / COMMFIFO.ASM < prev    next >
Assembly Source File  |  1991-12-08  |  44KB  |  1,445 lines

  1.     TITLE    COMM
  2.     PAGE    75,132
  3. ; Use AHD's handling of COM ports.  Wm. W. Plummer, 11/29/91
  4. ; Buffer sizes reduced and required to be 2**N.  Wm. W. Plummer, 11/11/91
  5. ; Accomodate V.24 requirements on DTR flaps.  Wm. W. Plummer 10/15/91
  6. ; Revised DTR_ON_COM to solve user problem.  Wm. W. Plummer, 10/3/91
  7. ; Make time delays independent of CPU speed.  Wm. W. Plummer, 9/16/91
  8. ; Use interrupts to trace CD, DSR, Wm. W. Plummer, 9/16/91
  9. ; Remove modem control from TX_INT. Wm. W. Plummer, 9/13/91
  10. ; Completely redo the XOFF/XON logic.  Too many races before. Wm. W. Plummer
  11. ; Revise interrupt dispatch for speed & function.  William W. Plummer, 9/12/91
  12. ; Merge in ahd's changes to flush control Q,S when received as flow control
  13. ; SEND buffer allows one byte for a SENDII call.  Avoids flow control
  14. ;  lockups. - William W. Plummer, 8/30/91
  15. ; Support for NS16550A chip with SILO - William W. Plummer, 8/30/91
  16. ; Add modem_status() routine - William W. Plummer, 7/2/91
  17. ; Put wrong code under AHD conditional - William W. Plummer, 7/2/91
  18. ; Change TITLE, repair bad instr after INST3 - William W. Plummer, 7/1/91
  19. ; Modified to use COM1 thru COM4 - William W. Plummer, 2/21/91
  20. ; Eliminate (incomplete) support for DOS1 - William W. Plummer, 11/13/90
  21.  
  22. ; Changes may be copied and modified with no notice.  Copyrights and copylefts
  23. ; are consider silly and do not apply.    --  William W. Plummer
  24.  
  25. ; modified to use MSC calling sequence.  jrr 3/86
  26. ;****************************************************************************
  27. ; Communications Package for the IBM PC, XT, AT, PCjr and strict compatibles.
  28. ; May be copied and used freely -- This is a public domain program
  29. ; Developed by Richard Gillmann, John Romkey, Jerry Saltzer,
  30. ; Craig Milo Rogers, Dave Mitton and Larry Afrin.
  31. ;
  32. ; We'd sure like to see any improvements you might make.
  33. ; Please send all comments and queries about this package
  34. ; to GILLMANN@USC-ISIB.ARPA
  35. ;
  36. ; o Supports both serial ports simultaneously
  37. ; o All speeds to 19200 baud
  38. ; o Compatible with PC, XT, AT and PCjr.
  39. ; o Built in XON/XOFF flow control option
  40. ; o Assembly language calling conventions
  41. ; o Logs all comm errors
  42. ; o Direct connect or modem protocol
  43.     PAGE ;
  44. ;
  45. ; MAXIMUM BUFFER SIZES -- *** MUST be powers of 2 ****
  46. R_SIZE    EQU    1024        ; Recv buffer size
  47. S_SIZE    EQU    256        ; Send buffer size
  48. ; INTERRUPT NUMBERS
  49. INT_COM1 EQU    0CH        ; COM1: FROM 8259
  50. INT_COM2 EQU    0BH        ; COM2: FROM 8259
  51. INT_COM3 EQU    0CH        ; COM3: FROM 8259
  52. INT_COM4 EQU    0BH        ; COM4: FROM 8259
  53. ; 8259 PORTS
  54. INTA00    EQU    20H        ; 8259A PORT, A0 = 0
  55. INTA01    EQU    21H        ; 8259A PORT, A0 = 1
  56. ; COM1: & COM3: LEVEL 4
  57. IRQ4    EQU    2*2*2*2     ; 8259A OCW1 MASK, M4=1, A0=0
  58. NIRQ4    EQU    NOT IRQ4 AND 0FFH ; COMPLEMENT OF ABOVE
  59. EOI4    EQU    4 OR 01100000B    ; 8259A OCW2 SPECIFIC IRQ4 EOI, A0=0
  60. ; COM2: & COM4: LEVEL 3
  61. IRQ3    EQU    2*2*2        ; 8259A OCW1 MASK, M3=1, A0=0
  62. NIRQ3    EQU    NOT IRQ3 AND 0FFH ; COMPLEMENT OF ABOVE
  63. EOI3    EQU    3 OR 01100000B    ; 8259A OCW2 SPECIFIC IRQ3 EOI, A0=0
  64.  
  65. ; FLOW CONTROL CHARACTERS
  66. CONTROL_Q EQU    11H        ; XON
  67. CONTROL_S EQU    13H        ; XOFF
  68. ; MISC.
  69. DOS    EQU    21H        ; DOS FUNCTION CALLS
  70.     PAGE
  71. ;
  72. ; ROM BIOS Data Area
  73. ;
  74. RBDA    SEGMENT AT 40H
  75. RS232_BASE DW    4 DUP(?)    ; ADDRESSES OF RS232 ADAPTERS
  76. RBDA    ENDS
  77. ;
  78. ; ROM PC-Type IDENT
  79. ;
  80. ROM        SEGMENT AT 0F000H
  81.         ORG    0FFFEH
  82. ROMID        DB    ?    ; 0FFH=PC OR EARLY XT, 0FEH=XT, 0FDH=JR
  83. ROM        ENDS
  84.         PAGE ;
  85. ;
  86. ; TABLE FOR EACH SERIAL PORT
  87. ;
  88. SP_TAB        STRUC
  89. PORT        DB    ?    ; 1 OR 2 OR 3 OR 4
  90. ; PARAMETERS FOR THIS INTERRUPT LEVEL
  91. INT_COM     DB    ?    ; INTERRUPT NUMBER
  92. IRQ        DB    ?    ; 8259A OCW1 MASK
  93. NIRQ        DB    ?    ; COMPLEMENT OF ABOVE
  94. EOI        DB    ?    ; 8259A OCW2 SPECIFIC END OF INTERRUPT
  95. ; INTERRUPT HANDLERS FOR THIS LEVEL
  96. INT_HNDLR    DW    ?    ; OFFSET TO INTERRUPT HANDLER
  97. OLD_COM_OFF    DW    ?    ; OLD HANDLER'S OFFSET
  98. OLD_COM_SEG    DW    ?    ; OLD HANDLER'S SEGMENT
  99. ; ATTRIBUTES
  100. INSTALLED    DB    ?    ; IS PORT INSTALLED ON THIS PC? (1=YES,0=NO)
  101. BAUD_RATE    DW    ?    ; 19200 MAX
  102. CONNECTION    DB    ?    ; M(ODEM), D(IRECT)
  103. PARITY        DB    ?    ; N(ONE), O(DD), E(VEN), S(PACE), M(ARK)
  104. STOP_BITS    DB    ?    ; 1, 2
  105. XON_XOFF    DB    ?    ; E(NABLED), D(ISABLED)
  106. ; FLOW CONTROL STATE
  107. HOST_OFF    DB    ?    ; HOST XOFF'ED (1=YES,0=NO)
  108. PC_OFF        DB    ?    ; PC XOFF'ED (1=YES,0=NO)
  109. URGENT_SEND    DB    ?    ; We MUST send one byte (XON/XOFF)
  110. SEND_OK     DB    ?    ; DSR and CTS are ON
  111. ; ERROR COUNTS
  112. ERROR_BLOCK    DW    8 DUP(?); EIGHT ERROR COUNTERS
  113.  
  114. ; UART PORTS - DATREG thru MSR must be in order shown.
  115. DATREG        DW    ?    ; DATA REGISTER
  116. IER        DW    ?    ; INTERRUPT ENABLE REGISTER
  117. IIR        DW    ?    ; INTERRUPT IDENTIFICATION REGISTER (RO)
  118. LCR        DW    ?    ; LINE CONTROL REGISTER
  119. MCR        DW    ?    ; MODEM CONTROL REGISTER
  120. LSR        DW    ?    ; LINE STATUS REGISTER
  121. MSR        DW    ?    ; MODEM STATUS REGISTER
  122. FCR        EQU    IIR    ; FIFO Control Register (WO)
  123.  ; Bits in FCR for NS16550A UART.  Note that writes to FCR are ignored
  124.  ; by other chips.
  125.  FIFO_ENABLE    EQU    001H    ; Enable FIFO mode
  126.  FIFO_CLR_RCV    EQU    002H    ; Clear receive FIFO
  127.  FIFO_CLR_XMT    EQU    004H    ; Clear transmit FIFO
  128.  FIFO_STR_DMA    EQU    008H    ; Start DMA Mode
  129.  ; 10H and 20H bits are register bank select on some UARTs (not handled)
  130.  FIFO_SZ_4    EQU    040H    ; Warning level is 4 before end
  131.  FIFO_SZ_8    EQU    080H    ; Warning level is 8 before end
  132.  FIFO_SZ_14    EQU    0C0H    ; Warning level is 14 before end
  133.  FIFO_SZ_MASK    EQU    0C0H    ; Size mask
  134.  ;
  135.  ; Commands used in code to operate FIFO.  Made up as combinations of above
  136.  ;
  137.  FIFO_CLEAR    EQU    0    ; Turn off FIFO
  138.  FIFO_SETUP    EQU    FIFO_SZ_14 OR FIFO_ENABLE
  139.  FIFO_INIT    EQU    FIFO_SETUP OR FIFO_CLR_RCV OR FIFO_CLR_XMT
  140.  ;
  141.  ; Miscellaneous FIFO-related stuff
  142.  ;
  143. FIFO_ENABLED    EQU    0C0H    ; 16550 makes these equal FIFO_ENABLE
  144. FIFO_LEN    EQU    16    ; Length of the FIFOs in a 16550A
  145. UART_SILO_LEN    DB    ?    ; Size of a silo chunk (1 for 8250)
  146. ;
  147. ; BUFFER POINTERS
  148. START_TDATA    DW    ?    ; INDEX TO FIRST CHARACTER IN X-MIT BUFFER
  149. END_TDATA    DW    ?    ; INDEX TO FIRST FREE SPACE IN X-MIT BUFFER
  150. START_RDATA    DW    ?    ; INDEX TO FIRST CHARACTER IN REC. BUFFER
  151. END_RDATA    DW    ?    ; INDEX TO FIRST FREE SPACE IN REC. BUFFER
  152. ; BUFFER COUNTS
  153. SIZE_TDATA    DW    ?    ; NUMBER OF CHARACTERS IN X-MIT BUFFER
  154. SIZE_RDATA    DW    ?    ; NUMBER OF CHARACTERS IN REC. BUFFER
  155. ; BUFFERS
  156. TDATA        DB    S_SIZE DUP(?)    ; SEND BUFFER
  157. RDATA        DB    R_SIZE DUP(?)    ; RECEIVE BUFFER
  158. SP_TAB        ENDS
  159. ; SP_TAB EQUATES
  160. ; WE HAVE TO USE THESE BECAUSE OF PROBLEMS WITH STRUC
  161. EOVFLOW  EQU    ERROR_BLOCK    ; BUFFER OVERFLOWS
  162. EOVRUN        EQU    ERROR_BLOCK+2    ; RECEIVE OVERRUNS
  163. EBREAK        EQU    ERROR_BLOCK+4    ; BREAK CHARS
  164. EFRAME        EQU    ERROR_BLOCK+6    ; FRAMING ERRORS
  165. EPARITY  EQU    ERROR_BLOCK+8        ; PARITY ERRORS
  166. EXMIT        EQU    ERROR_BLOCK+10    ; TRANSMISSION ERRORS
  167. EDSR        EQU    ERROR_BLOCK+12    ; DATA SET READY ERRORS
  168. ECTS        EQU    ERROR_BLOCK+14    ; CLEAR TO SEND ERRORS
  169. DLL        EQU    DATREG        ; LOW DIVISOR LATCH
  170. DLH        EQU    IER        ; HIGH DIVISOR LATCH
  171.     PAGE ;
  172. ;    put the data in the DGROUP segment
  173. ;    far calls enter with DS pointing to DGROUP
  174. ;
  175. DGROUP    GROUP _DATA
  176. _DATA    SEGMENT PUBLIC 'DATA'
  177. ;
  178. DIV50PC  EQU    2304        ; DIVISOR FOR 50 BAUD (PC,XT)
  179. DIV50JR  EQU    2237        ; DIVISOR FOR 50 BAUD (JR)
  180. DIV50        DW    2304    ; ACTUAL DIVISOR FOR 50 BAUD IN USE
  181. CURRENT_AREA    DW    AREA1    ; CURRENTLY SELECTED AREA
  182. ; DATA AREAS FOR EACH PORT
  183. AREA1    SP_TAB    <1,INT_COM1,IRQ4,NIRQ4,EOI4>    ; COM1 DATA AREA
  184. AREA2    SP_TAB    <2,INT_COM2,IRQ3,NIRQ3,EOI3>    ; COM2 DATA AREA
  185. AREA3    SP_TAB    <3,INT_COM3,IRQ4,NIRQ4,EOI4>    ; COM3 DATA AREA
  186. AREA4    SP_TAB    <4,INT_COM4,IRQ3,NIRQ3,EOI3>    ; COM4 DATA AREA
  187. _DATA    ENDS
  188.     PAGE ;
  189. COM_TEXT    SEGMENT PARA PUBLIC 'CODE'
  190.     ASSUME    CS:COM_TEXT,DS:DGROUP,ES:NOTHING
  191.  
  192.     PUBLIC    _select_port
  193.     PUBLIC    _save_com
  194.     PUBLIC    _install_com
  195.     PUBLIC    _restore_com
  196.     PUBLIC    _open_com
  197.     PUBLIC    _close_com
  198.     PUBLIC    _dtr_on
  199.     PUBLIC    _dtr_off
  200.     PUBLIC    _r_count
  201.     PUBLIC    _s_count
  202.     PUBLIC    _receive_com
  203.     PUBLIC    _send_com
  204.     PUBLIC    _sendi_com
  205.     PUBLIC    _send_local
  206.     PUBLIC    _break_com
  207.     PUBLIC    _com_errors
  208.     PUBLIC    _modem_status
  209. IFDEF DEBUG
  210.     PUBLIC INST2, INST4
  211.     PUBLIC OC1, OC2, OCX
  212.     PUBLIC DTRON1, DTRON6, DTRONF, DTRONS, DTRONX
  213.     PUBLIC L11, LI3, LI4
  214.     PUBLIC RCVX
  215.     PUBLIC L4A, L44
  216.     PUBLIC WaitN, WaitN1, WaitN2
  217.     PUBLIC SENDII, SENDII2, SENDII3, SENDII4, SENDIIX
  218.     PUBLIC INT_COMMON, REPOLL, INT_END
  219.     PUBLIC LSTAT_INT
  220.     PUBLIC MSTAT_INT
  221.     PUBLIC TX_INT, TXI1, TXI2, TXI3, TXI9
  222.     PUBLIC RX_INT, RXI0, RXI1, RXI2, RXI6, RXIX
  223. ENDIF
  224.     PAGE ;
  225. ; Notes, thoughts and explainations by Bill Plummer.  These are intended to
  226. ; help those of you who would like to make modifications.
  227.  
  228. ; Here's the order of calls in UUPC.  The routines in COMM.ASM are called
  229. ; from ulib.c.
  230.  
  231. ; First (when a line in system has been read?), ulib&openline calls
  232. ;     select_port()        ; Sets up CURRENT_AREA
  233. ; then,  save_com()        ; Save INT vector
  234. ; then,  install_com()        ; Init area, hook INT
  235. ; then,  open_com(&cmd, 'D', 'N', STOP*T, 'D')  ; Init UART, clr bufs
  236. ; then,  dtr_on().
  237.  
  238. ; At that point the line is up and running.  UUPC calls ulib&sread()
  239. ; which calls,    receive_com();
  240.  
  241. ; And UUPC calls ulib&swrite()
  242. ; which calls,    send_com();
  243.  
  244. ; To cause an error that the receiver will see, UUPC calls ulib&ssendbrk();
  245. ; which calls,    break_com();
  246.  
  247. ; When all done with the line, UUPC calls ulib&closeline()
  248. ; which calls,    dtr_off();
  249. ; then,  close_com();
  250. ; then,  restore_com();     ; Unhook INT
  251. ; and,     stat_errors();
  252.  
  253.  
  254. ; Note: On the PC COM1 and COM3 share IRQ4, while COM2 and COM4 share IRQ3.
  255. ; BUT, only one device on a given IRQ line can be active at a time.  So it is
  256. ; sufficient for UUPC to hook whatever IRQ INT its modem is on as long as it
  257. ; unhooks it when it is done with that COM port.  COMM cannot be an installed
  258. ; device driver since it must go away when UUPC is done so that other devices
  259. ; on the same INTs will come back to life.  Also, it is OK to have a static
  260. ; CURRENT_AREA containing the current area that UUPC is using.
  261.  
  262. ; Note about using the NS16550A UART chip's FIFOs.  These are operated as
  263. ; silos.  In other words when an interrupt happens because the receive(send)
  264. ; FIFO is nearly full(empty), as many bytes as possible are transferred and
  265. ; the interrupt dismissed.  Thus, the interrupt load is lowered.
  266.  
  267.  
  268. ; Concerning the way the comm line is brought up.
  269. ; There are two basic cases, the Direct ('D') connection and the Modem ('M')
  270. ; connection.  For either UUPC calls dtr_on_com() to bring up the line.  This
  271. ; causes Data Terminal Ready (DTR) and Request To Send (RTS) to be set.  Note
  272. ; this is OK for a simple 3-wire link but may be REQUIRED for a COM port
  273. ; connected to an external modem.
  274.  
  275. ; The difference between a D connection and an M connection is
  276. ; whether or not the PC can expect any signals back from the modem.  If
  277. ; there is a simple 3-wire link, Data Set Ready will be floating.
  278. ; (Actually, some wise people jumper Data Terminal Read back to Data
  279. ; Set Ready so the PC sees its own DTR appear as DSR.)    UUPC should be
  280. ; able to handle the simplest cable as a design feature.  So both D and
  281. ; M connections send out DTR and RTS, but only the M connection expects
  282. ; a modem to respond.
  283.  
  284. ; Then, if it is full modem connection (M), we wait for a few
  285. ; seconds hoping that both Data Set Ready (DSR) and Clear To Send (CTS)
  286. ; will come up.  If they don't, the associated counters are incremented
  287. ; for subsequent printing in the error log.  Note that no error is
  288. ; reported from COMM to UUPC at this point, although this would be a
  289. ; good idea.  Instead UUPC storms ahead with its output trying to
  290. ; establish a link, but the output is never sent due to one of the
  291. ; control signals being false.    UUPC could check the modem status using
  292. ; a call which has been installed just for this purpose.
  293.  
  294. ; Note, if you are going to connect your PC running UUPC to,
  295. ; say, a mainframe and you need hardware flow control (i.e., RTS-CTS
  296. ; handshaking), use a Modem connection.  Using a simple 3-wire cable
  297. ; forbids hardware flow control and UUPC must be instructed to use a
  298. ; Direct connection.  Refer to comments in the sample MODEM file (*.MDM)
  299. ; on how to make this selection.
  300.  
  301. ; References used in designing the revisions to COMM.ASM:
  302. ;    1.    The UNIX fas.c Driver code.
  303. ;    2.    SLIP8250.ASM from the Clarkson driver set.
  304. ;    3.    NS16550A data sheet and AN-491 from National Semiconductor.
  305. ;    4.    Bell System Data Communications, Technical Reference for
  306. ;        Data Set 103A, Interface Specification, February, 1967
  307. ;    5.    Network mail regarding V.24
  308. ;    6.    Joe Doupnik, Utah State, primary author of MS-Kermit
  309.     PAGE ;
  310. ;
  311. ; SELECT WHICH PORT IS TO BE "ACTIVE"
  312. ;    [bp+6] = port number
  313. _select_port    PROC FAR
  314.     push bp
  315.     mov bp,sp
  316.     mov AX,[bp+6]            ; get aguement
  317.     CMP    AL,1            ; Port 1?
  318.      JE    SP1            ; Yes
  319.     CMP    AL,2            ; Port 2?
  320.      JE    SP2            ; Yes
  321.     CMP    AL,3            ; Port 3?
  322.      JE    SP3            ; Yes
  323.     CMP    AL,4            ; Port 4?
  324.      JE    SP4            ; Yes
  325.     INT 20H             ; N.O.T.A. ????? Halt for debugging!
  326.     ; Assume port 1 if continued
  327. SP1:    MOV    AX,OFFSET DGROUP:AREA1    ; SELECT COM1 DATA AREA
  328.     JMP    SHORT SPX        ; CONTINUE
  329. SP2:    MOV    AX,OFFSET DGROUP:AREA2    ; SELECT COM2 DATA AREA
  330.     JMP    SHORT SPX        ; CONTINUE
  331. SP3:    MOV    AX,OFFSET DGROUP:AREA3    ; SELECT COM3 DATA AREA
  332.     JMP    SHORT SPX        ; CONTINUE
  333. SP4:    MOV    AX,OFFSET DGROUP:AREA4    ; SELECT COM4 DATA AREA
  334.     ;Fall into SPX
  335. SPX:    MOV    CURRENT_AREA,AX     ; SET SELECTION IN MEMORY
  336.     mov sp,bp
  337.     pop bp
  338.     RET
  339. _select_port    ENDP
  340.     PAGE ;
  341. ;
  342. ; SAVE ORIGINAL COM VECTOR
  343. ;
  344. _save_com    PROC FAR
  345.     push bp
  346.     mov bp,sp
  347.     push si
  348.     PUSH    ES            ; SAVE EXTRA SEGMENT
  349.     MOV    SI,CURRENT_AREA     ; SI POINTS TO DATA AREA
  350.     MOV    AREA1.INT_HNDLR,OFFSET INT_HNDLR1
  351.     MOV    AREA2.INT_HNDLR,OFFSET INT_HNDLR2
  352.     MOV    AREA3.INT_HNDLR,OFFSET INT_HNDLR3
  353.     MOV    AREA4.INT_HNDLR,OFFSET INT_HNDLR4
  354.  
  355. ; Save old interrupt vector
  356.     MOV    AH,35H            ; FETCH INTERRUPT VECTOR CONTENTS
  357.     MOV    AL,INT_COM[SI]        ; INTERRUPT NUMBER
  358.     INT    DOS            ; DOS 2 FUNCTION
  359.     MOV    OLD_COM_OFF[SI],BX    ; SAVE
  360.     MOV    BX,ES            ; ES:BX
  361.     MOV    OLD_COM_SEG[SI],BX    ; FOR LATER RESTORATION
  362.     POP    ES            ; RESTORE ES
  363.     pop si
  364.     mov sp,bp
  365.     pop bp
  366.     RET                ; DONE
  367. _save_com    ENDP
  368.     PAGE ;
  369. ;
  370. ; INSTALL_COM:    INSTALL THE ACTIVE PORT
  371. ;
  372. ; SET UART PORTS FROM RS-232 BASE IN ROM BIOS DATA AREA
  373. ; INITIALIZE PORT CONSTANTS AND ERROR COUNTS
  374. ; INSTALL INTERRUPT VECTOR
  375. ;
  376. ;    return ax=1 on success ax=0 on failure
  377. ;
  378. _install_com    PROC FAR
  379.     push bp
  380.     mov bp,sp
  381.     push si
  382.     PUSH BX
  383.     PUSH CX
  384.     PUSH DX
  385.     MOV    SI,CURRENT_AREA     ; SI POINTS TO DATA AREA
  386.     PUSH    ES            ; SAVE EXTRA SEGMENT
  387.     CMP    INSTALLED[SI],1     ; Is port installed on this machine?
  388.      JNE    INSTOK            ; NO, CONTINUE
  389.     JMP    INST9            ; ELSE JUMP IF ALREADY INSTALLED
  390. ;
  391. ; CLEAR ERROR COUNTS
  392. INSTOK: MOV    WORD PTR EOVFLOW[SI],0    ; BUFFER OVERFLOWS
  393.     MOV    WORD PTR EOVRUN[SI],0    ; RECEIVE OVERRUNS
  394.     MOV    WORD PTR EBREAK[SI],0    ; BREAK CHARS
  395.     MOV    WORD PTR EFRAME[SI],0    ; FRAMING ERRORS
  396.     MOV    WORD PTR EPARITY[SI],0    ; PARITY ERRORS
  397.     MOV    WORD PTR EXMIT[SI],0    ; TRANSMISSION ERRORS
  398.     MOV    WORD PTR EDSR[SI],0    ; DATA SET READY ERRORS
  399.     MOV    WORD PTR ECTS[SI],0    ; CLEAR TO SEND ERRORS
  400. ; SENSE PC TYPE AND SET DIVISOR ACCORDINGLY
  401.     MOV    BX,ROM            ; HIGH ROM SEGMENT
  402.     MOV    ES,BX            ; TO ES
  403.     ASSUME    ES:ROM
  404.     MOV    DIV50,DIV50PC        ; ASSUME PC OR XT
  405.     CMP    ROMID,0FDH        ; IS IT A PCjr?
  406.      JNE    INST0            ; JUMP IF NOT
  407.     MOV    DIV50,DIV50JR        ; ELSE SET JR DIVISOR
  408. ; SET UART PORT ADDRESSES
  409. INST0:    MOV    BX,RBDA         ; ROM BIOS DATA AREA
  410.     MOV    ES,BX            ; TO ES
  411.     ASSUME    ES:RBDA
  412. ;
  413. ; Map port number (COMx) into IO Address using the RS232_Base[x] table in
  414. ; the BIOS data area.  If any of the ports is missing there should be a
  415. ; zero in the table for this COM port.    BIOS startup routines pack the table
  416. ; so that if you have a COM4 but no COM3, 2E8 will be found in 40:4 and 0
  417. ; will be in 40:6.
  418.  
  419. ; N.B. The exact IO address in 40:x is irrelevant and may well be something
  420. ; other than the "standard" values if specially designed hardware is used.
  421. ; To minimize flack, we will use the standard value if the slot in the table
  422. ; is 0.  The bad side effect of this is that (in the standard losing case of
  423. ; a COM4 but no COM3) both COM3 and COM4 will reference the hardware at 2E8.
  424.  
  425.     CMP    PORT[SI],1        ; PORT 1?
  426.      JE    INST3F8         ; Yes
  427.     CMP    PORT[SI],2        ; PORT 2?
  428.      JE    INST2F8         ; Yes
  429.     CMP    PORT[SI],3        ; PORT 3?
  430.      JE    INST3E8         ; Yes
  431.     CMP    PORT[SI],4        ; PORT 4?
  432.      JE    INST2E8         ; Yes
  433.     INT    20H            ; NOTA. (Caller is screwed up badly)
  434.  
  435. INST3F8:MOV AX,3F8H            ; Standard COM1 location
  436.     CMP    RS232_BASE+0,0000H    ; We have information?
  437.      JE    INST2            ; No --> Use default
  438.     MOV    AX,RS232_BASE+0     ; Yes --> Use provided info
  439.     JMP    SHORT INST2        ; CONTINUE
  440.  
  441. INST2F8:MOV AX,2F8H            ; Standard COM2 location
  442.     CMP    RS232_BASE+2,0000H    ; We have information?
  443.      JE    INST2            ; No --> Use default
  444.     MOV    AX,RS232_BASE+2     ; Yes --> Use provided info
  445.     JMP    SHORT INST2        ; CONTINUE
  446.  
  447. INST3E8:MOV AX,3E8H            ; Standard COM3 location
  448.     CMP    RS232_BASE+4,0000H    ; We have information?
  449.      JE    INST2            ; No --> Use default
  450.     MOV    AX,RS232_BASE+4     ; Yes --> Use provided info
  451.     JMP    SHORT INST2        ; CONTINUE
  452.  
  453. INST2E8:MOV AX,2E8H            ; Standard COM4 location
  454.     CMP    RS232_BASE+6,0000H    ; We have information?
  455.      JE    INST2            ; No --> Use default
  456.     MOV    AX,RS232_BASE+6     ; Yes --> Use provided info
  457.     ; Fall into INST2
  458.  
  459.  
  460. ; Now we have an IO address for the COMx that we want to use.  If it is
  461. ; anywhere in RS232_Base, we know that it has been check and is OK to use.
  462. ; So, even if my 2E8 (COM4) appears in 40:6 (normally for COM3), I can use
  463. ; it.
  464.  
  465. INST2:    CMP    AX,RS232_BASE        ; INSTALLED?
  466.      JE    INST2A            ; JUMP IF SO
  467.     CMP    AX,RS232_BASE+2     ; INSTALLED?
  468.      JE    INST2A            ; JUMP IF SO
  469.     CMP    AX,RS232_BASE+4     ; INSTALLED?
  470.      JE    INST2A            ; JUMP IF SO
  471.     CMP    AX,RS232_BASE+6     ; INSTALLED?
  472.      JNE    INST666         ; JUMP IF NOT
  473.     ; Fall into INST2A
  474.  
  475. INST2A: MOV    BX,DATREG        ; OFFSET OF TABLE OF PORTS
  476.     MOV    CX,7            ; LOOP SIX TIMES
  477. INST3:    MOV    WORD PTR [SI][BX],AX    ; SET PORT ADDRESS
  478.     INC    AX            ; NEXT PORT
  479.     ADD    BX,2            ; NEXT WORD ADDRESS
  480.      LOOP    INST3            ; RS232 BASE LOOP
  481.     MOV DX,FCR[SI]            ; Get FIFO Control Register
  482.     MOV AL,FIFO_INIT
  483.     OUT DX,AL            ; Try to initialize the FIFO
  484.     MOV DX,IIR[SI]            ; Get interrupt ID register
  485.     IN AL,DX            ; See how the UART responded
  486.     AND CL,FIFO_ENABLED        ; Keep only these bits
  487.     MOV CX,1            ; Assume chunk size of 1 for 8250 case
  488.     CMP AL,FIFO_ENABLED        ; See if 16550A
  489.      JNE INST4            ; Jump if not
  490. IFE (FIFO_SETUP AND FIFO_SZ_MASK)
  491.     MOV CX,FIFO_LEN-FIFO_LEN
  492. ELSEIFE (FIFO_SETUP AND FIFO_SZ_MASK - FIFO_SZ_4)
  493.     MOV CX,FIFO_LEN-4
  494. ELSEIFE (FIFO_SETUP AND FIFO_SZ_MASK - FIFO_SZ_8)
  495.     MOV CX,FIFO_LEN-8
  496. ELSEIFE (FIFO_SETUP AND FIFO_SZ_MASK - FIFO_SZ_14)
  497.     MOV CX,FIFO_LEN-14
  498. ENDIF
  499. INST4:    MOV UART_SILO_LEN[SI],CL    ; Save chunk size
  500.     MOV AL,FIFO_CLEAR
  501.     OUT DX,AL
  502.  
  503.     MOV    AH,25H            ; SET INTERRUPT VECTOR CONTENTS
  504.     MOV    AL,INT_COM[SI]        ; INTERRUPT NUMBER
  505.     MOV    DX,INT_HNDLR[SI]    ; OUR INTERRUPT HANDLER [WWP]
  506.     PUSH    DS            ; SAVE DATA SEGMENT
  507.     PUSH    CS            ; COPY CS
  508.     POP    DS            ; TO DS
  509.     INT    DOS            ; DOS FUNCTION
  510.     POP    DS            ; RECOVER DATA SEGMENT
  511.  
  512. ; PORT INSTALLED
  513. INST9:    MOV AX,1
  514.     JMP SHORT INSTX
  515.  
  516. ; PORT NOT INSTALLED
  517. INST666:MOV AX,0
  518.     ;Fall into INSTX
  519.  
  520. ; Common exit
  521. INSTX:    MOV INSTALLED[SI],AL        ; Indicate whether installed or not
  522.     POP DX
  523.     POP CX
  524.     POP BX
  525.     POP ES
  526.     pop si
  527.     mov sp,bp
  528.     pop bp
  529.     RET
  530. _install_com    ENDP
  531.     PAGE ;
  532. ;
  533. ; RESTORE ORIGINAL INTERRUPT VECTOR
  534. ;
  535. _restore_com    PROC FAR
  536.     push bp
  537.     mov bp,sp
  538.     push si
  539.     PUSH BX
  540.     MOV    SI,CURRENT_AREA     ; SI POINTS TO DATA AREA
  541.     MOV    INSTALLED[SI],0     ; PORT IS NO LONGER INSTALLED
  542.     MOV    AH,25H            ; SET INTERRUPT VECTOR FUNCTION
  543.     MOV    AL,INT_COM[SI]        ; INTERRUPT NUMBER
  544.     MOV    DX,OLD_COM_OFF[SI]    ; OLD OFFSET TO DX
  545.     MOV    BX,OLD_COM_SEG[SI]    ; OLD SEG
  546.     PUSH    DS            ; SAVE DS
  547.     MOV    DS,BX            ; TO DS
  548.     INT    DOS            ; DOS FUNCTION
  549.     POP    DS            ; RECOVER DS
  550.     POP BX
  551.     pop si
  552.     mov sp,bp
  553.     pop bp
  554.     RET
  555. _restore_com    ENDP
  556.     PAGE ;
  557. ;
  558. ; OPEN_COM ON CURRENT PORT
  559. ;
  560. ; CLEAR BUFFERS
  561. ; RE-INITIALIZE THE UART
  562. ; ENABLE INTERRUPTS ON THE 8259 INTERRUPT CONTROL CHIP
  563. ;
  564. ; [bp+6] = BAUD RATE
  565. ; [bp+8] = CONNECTION: M(ODEM), D(IRECT)
  566. ; [bp+10] = PARITY:    N(ONE), O(DD), E(VEN), S(PACE), M(ARK)
  567. ; [bp+12] = STOP BITS:    1, 2
  568. ; [bp+14] = XON/XOFF:    E(NABLED), D(ISABLED)
  569. ;
  570. _open_com    PROC FAR
  571.     push bp
  572.     mov bp,sp
  573.     push si
  574.     PUSHF
  575.     PUSH BX
  576.     PUSH DX
  577.     MOV    SI,CURRENT_AREA     ; SI POINTS TO DATA AREA
  578.  
  579.     CLI                ; INTERRUPTS OFF
  580.     mov ax,[bp+6]
  581.     MOV    BAUD_RATE[SI],AX    ; SET
  582.     mov bh,[bp+8]
  583.     MOV    CONNECTION[SI],BH    ; ARGS
  584.     mov bl,[bp+10]
  585.     MOV    PARITY[SI],BL        ; IN
  586.     mov ch,[bp+12]
  587.     MOV    STOP_BITS[SI],CH    ; MEMORY
  588.     mov cl,[bp+14]
  589.     MOV    XON_XOFF[SI],CL
  590.  
  591.  
  592.  
  593. ; RESET FLOW CONTROL
  594.     MOV    HOST_OFF[SI],0        ; HOST FLOWING
  595.     MOV    PC_OFF[SI],0        ; PC FLOWING
  596.     MOV URGENT_SEND[SI],0        ; No flow ctl which must go out
  597.     MOV SEND_OK[SI],0        ; DTR&CTS are not on yet
  598.  
  599. ; RESET BUFFER COUNTS AND POINTERS
  600.     MOV    START_TDATA[SI],0
  601.     MOV    END_TDATA[SI],0
  602.     MOV    START_RDATA[SI],0
  603.     MOV    END_RDATA[SI],0
  604.     MOV    SIZE_TDATA[SI],0
  605.     MOV    SIZE_RDATA[SI],0
  606.  
  607.     TEST    INSTALLED[SI],1     ; PORT INSTALLED?
  608.      JNZ    OC1            ; SKIP IF SO
  609.     JMP    OCX            ; ELSE ABORT
  610.  
  611. ; RESET THE UART
  612. OC1:    MOV DX,MCR[SI]            ; Modem Control Register
  613.     MOV AL,0            ; Clr DTR, RTS, OUT1, OUT2 & LOOPBACK
  614.     OUT DX,AL
  615.     MOV AL,30H            ; Be sure DSR and CTS are ON.  Some
  616.     MOV DX,MSR[SI]            ; modems need this (I don't know why!)
  617.     OUT DX,AL            ; Clear Loopback, etc.
  618.     JMP    $+2            ; I/O DELAY FOR JR
  619.     IN AL,DX            ; Read to get the delta bits & clr int
  620.     AND AL,30H            ; Leave the two critical bits
  621.     CMP AL,30H            ; Both on?
  622.      JNE OC2            ; No.  Leave SEND_OK zero.
  623.     MOV SEND_OK[SI],1        ; Allow TX_INT to send out data
  624. OC2:    MOV    DX,FCR[SI]        ; I/O Address of FIFO control register
  625.     MOV    AL,FIFO_CLEAR        ; Disable FIFOs
  626.     OUT    DX,AL            ; Non-16550A chips will ignore this
  627.     MOV    DX,LSR[SI]        ; RESET LINE STATUS CONDITION
  628.     IN    AL,DX
  629.     JMP    $+2            ; I/O DELAY FOR JR
  630.     MOV    DX,DATREG[SI]        ; RESET RECEIVE DATA CONDITION
  631.     IN    AL,DX
  632.     JMP    $+2            ; I/O DELAY FOR JR
  633.     MOV    DX,MSR[SI]        ; RESET MODEM DELTAS AND CONDITIONS
  634.     IN    AL,DX
  635.  
  636. ; CONVERT PASSED BAUD RATE TO UART DIVISOR
  637.     MOV    AX,50            ; 50 BAUD
  638.     MUL    DIV50            ; TIMES ITS DIVISOR
  639.     DIV    BAUD_RATE[SI]        ; OTHER SPEEDS ARE PROPORTIONAL
  640.     MOV    BX,AX            ; RESULT TO BX
  641.  
  642. ; SET UART DIVISOR
  643.     MOV    DX,LCR[SI]        ; LINE CONTROL REGISTER
  644.     MOV    AL,80H            ; HI BIT ON
  645.     OUT    DX,AL            ; SET DLAB = 1
  646.     JMP    $+2            ; I/O DELAY FOR JR
  647.     MOV    DX,WORD PTR DLL[SI]    ; LEAST SIGNIFICANT BYTE
  648.     MOV    AL,BL            ; LSB FROM TABLE
  649.     OUT    DX,AL            ; SET LSB ON UART
  650.     JMP    $+2            ; I/O DELAY FOR JR
  651.     MOV    DX,WORD PTR DLH[SI]    ; MOST SIGNIFICANT BYTE
  652.     MOV    AL,BH            ; MSB FROM TABLE
  653.     OUT    DX,AL            ; SET MSB ON UART
  654.     JMP    $+2            ; I/O DELAY FOR JR
  655.  
  656. ; SET PARITY AND NUMBER OF STOP BITS
  657.     MOV    AL,03H            ; NONE OR SPACE PARITY IS THE DEFAULT
  658.     CMP    PARITY[SI],'O'          ; ODD PARITY REQUESTED?
  659.      JNE    P1            ; JUMP IF NOT
  660.     MOV    AL,0AH            ; SELECT ODD PARITY
  661.     JMP    SHORT P3        ; CONTINUE
  662. ;
  663. P1:    CMP    PARITY[SI],'E'          ; EVEN PARITY REQUESTED?
  664.      JNE    P2            ; JUMP IF NOT
  665.     MOV    AL,1AH            ; SELECT EVEN PARITY
  666.     JMP    SHORT P3        ; CONTINUE
  667. ;
  668. P2:    CMP    PARITY[SI],'M'          ; MARK PARITY REQUESTED?
  669.      JNE    P3            ; JUMP IF NOT
  670.     MOV    AL,2AH            ; SELECT MARK PARITY
  671. P3:    TEST    STOP_BITS[SI],2     ; 2 STOP BITS REQUESTED?
  672.      JZ    STOP1            ; NO
  673.     OR    AL,4            ; YES
  674. STOP1:    MOV    DX,LCR[SI]        ; LINE CONTROL REGISTER
  675.     OUT    DX,AL            ; SET UART PARITY MODE AND DLAB=0
  676.  
  677. ; Initialize the FIFOs
  678.     MOV    DX,FCR[SI]        ; I/O Address of FIFO control register
  679.     MOV    AL,FIFO_INIT        ; Clear FIFOs, set size, enable FIFOs
  680.     OUT    DX,AL            ; Non-16550A chips will ignore this
  681.  
  682. ; ENABLE INTERRUPTS ON 8259 AND UART
  683.     IN    AL,INTA01        ; SET ENABLE BIT ON 8259
  684.     AND    AL,NIRQ[SI]
  685.     OUT    INTA01,AL
  686.     MOV DX,IER[SI]            ; Interrupt enable register
  687.     MOV AL,0FH            ; Line & Modem status, recv, send
  688.     OUT DX,AL            ; Enable all interrupts
  689.     JMP    $+2            ; I/O DELAY FOR JR
  690.  
  691. OCX:    STI                ; INTERRUPTS ON
  692.     POP DX
  693.     POP BX
  694.     POPF
  695.     pop si
  696.     mov sp,bp
  697.     pop bp
  698.     RET                ; DONE
  699. _open_com    ENDP
  700.     PAGE ;
  701. ;
  702. ; CLOSE_COM - TURNS OFF INTERRUPTS FROM THE COMMUNICATIONS PORT
  703. ;
  704. _close_com    PROC FAR
  705.     push bp
  706.     mov bp,sp
  707.     push si
  708.     PUSH DX
  709.     MOV    SI,CURRENT_AREA     ; SI POINTS TO DATA AREA
  710.     TEST    INSTALLED[SI],1     ; PORT INSTALLED?
  711.      JZ    CCX            ; ABORT IF NOT
  712.  
  713. ; TURN OFF UART and clear FIFOs in NS16550A
  714.     MOV DX,IER[SI]
  715.     MOV AL,0
  716.     OUT DX,AL            ; No interrupts right now, please
  717.     MOV AL,FIFO_CLEAR
  718.     OUT DX,AL
  719.  
  720. ; TURN OFF 8259
  721.     MOV    DX,INTA01
  722.     IN    AL,DX
  723.     OR    AL,IRQ[SI]
  724.     JMP    $+2            ; DELAY FOR AT
  725.     OUT    DX,AL
  726.  
  727. CCX:    POP DX
  728.     pop si
  729.     mov sp,bp
  730.     pop bp
  731.     RET
  732. _close_com    ENDP
  733.     PAGE ;
  734. ;
  735. ; DTR_OFF - TURNS OFF DTR TO TELL MODEMS THAT THE TERMINAL HAS GONE AWAY
  736. ;        AND TO HANG UP THE PHONE
  737. ;
  738. _dtr_off    PROC FAR
  739.     push bp
  740.     mov bp,sp
  741.     push si
  742.     PUSHF                ; SAVE FLAGS
  743.     PUSH    AX            ; SAVE REGS
  744.     PUSH    DX
  745.     PUSH    SI
  746.     MOV    SI,CURRENT_AREA     ; SI POINTS TO DATA AREA
  747.     TEST    INSTALLED[SI],1     ; PORT INSTALLED?
  748.      JZ    DFX            ; ABORT IF NOT
  749.  
  750.     MOV DX,MCR[SI]            ; Modem Control Register
  751.     IN AL,DX            ; See if it is on
  752.     AND AL,1            ; Just look at the DTR bit
  753.      JE DFX             ; Not on.  Don't clr.  Don't wait.
  754.     MOV AL,08H            ; DTR off, RTS off, OUT2 on
  755.     OUT DX,AL
  756. DFX:    POP    SI            ; RECOVER REGS
  757.     POP    DX
  758.     POP    AX
  759.     POPF                ; RECOVER FLAGS
  760.     pop si
  761.     mov sp,bp
  762.     pop bp
  763.     RET
  764. _dtr_off    ENDP
  765.     PAGE ;
  766. ;
  767. ; DTR_ON - TURNS DTR ON
  768. ;
  769. _dtr_on PROC FAR
  770.     push bp
  771.     mov bp,sp
  772.     push si
  773.     PUSHF                ; SAVE FLAGS
  774.     PUSH    AX            ; SAVE REGS
  775.     PUSH BX
  776.     PUSH    DX
  777.     PUSH    SI
  778.     MOV    SI,CURRENT_AREA     ; SI POINTS TO DATA AREA
  779.     TEST    INSTALLED[SI],1     ; PORT INSTALLED?
  780.      JZ DTRONF            ; Suppress output if not
  781.  
  782. ; Tell modem we are ready and want to send with line idle
  783.  
  784.     MOV DX,MCR[SI]            ; Modem Control Register
  785.     MOV AL,00001011B        ; OUT 2, RTS, DTR
  786.     OUT DX,AL
  787.     JMP    $+2            ; I/O DELAY FOR JR
  788.  
  789. ; Wait for awhile to give the modem time to respond
  790.  
  791.     MOV AH,2CH            ; Get time (H:M:S:H to CH:CL:DH:DL)
  792.     INT 21H
  793.     MOV BX,DX            ; Save seconds&hundreths
  794.     ADD BH,05            ; Allow 5 seconds wait (actually 4-6)
  795.     CMP BH,60            ; Wrap around check
  796.      JL DTRON1            ; No wrap
  797.     SUB BH,60
  798. DTRON1: CMP SEND_OK[SI],1        ; Did the modem come up?
  799.      JE DTRONS            ; Yes.    Both DSR and CTS are true.
  800.     INT 21H             ; Get the time again
  801.     CMP DX,BX            ; Current time is passed the deadline?
  802.      JB DTRON1            ; No, keep checking 'til time runs out
  803.     MOV AX,200H            ; 2 Seconds
  804.     CALL WaitN            ; V.24 says 2 sec hi before data
  805.  
  806.     CMP CONNECTION[SI],'D'          ; Direct connection (no DSR,CTS)?
  807.      JE DTRONS            ; Skip DSR, CTS check
  808.  
  809.      ; Modem failed to come up.  Bump counts that tell why.
  810.     MOV    DX,MSR[SI]        ; MODEM STATUS REGISTER
  811.     IN    AL,DX            ; GET MODEM STATUS
  812.     TEST    AL,20H            ; DATA SET READY?
  813.      JNZ DTRON6            ; Yup.
  814.     INC    WORD PTR EDSR[SI]    ; BUMP ERROR COUNT
  815. DTRON6: TEST    AL,10H            ; Clear To Send?
  816.      JNZ DTRONF            ; That's OK.
  817.     INC    WORD PTR ECTS[SI]    ; BUMP ERROR COUNT - WE TIMED OUT
  818.     ; Fall into DTRONF
  819.  
  820. ; Failure return
  821.  
  822. DTRONF: MOV SEND_OK[SI],0        ; Block output data flow at TX_INT
  823.     JMP SHORT DTRONX
  824.  
  825.  
  826. ; Successful return
  827.  
  828. DTRONS: MOV SEND_OK[SI],1        ; Enable output
  829.     ; Fall into DTRONX
  830.  
  831. DTRONX: POP    SI            ; RECOVER REGS
  832.     POP    DX
  833.     POP BX
  834.     POP    AX
  835.     POPF                ; RECOVER FLAGS
  836.     pop si
  837.     mov sp,bp
  838.     pop bp
  839.     RET
  840. _dtr_on ENDP
  841.     PAGE ;
  842. ;
  843. ; Wait for specified time using the 18.2 ticks/second clock
  844. ;
  845. ; Call:     AX has seconds,hundreths
  846. ;        CALL WaitN
  847. ; Return:    At least the requested time has passed
  848. ;
  849.  
  850. WaitN    PROC NEAR
  851.     PUSH BP
  852.     MOV BP,SP
  853.     PUSH AX
  854.     PUSH BX
  855.     PUSH CX
  856.     PUSH DX
  857.     PUSH AX             ; Save a copy of the arg
  858.     MOV AH,2CH            ; Get time (H:M:S:H to CH:CL:DH:DL)
  859.     INT DOS
  860.     POP BX                ; Recover S:H arg
  861.     ADD BX,DX            ; Determine deadline
  862.     CMP BL,100            ; Wrap around?
  863.      JL WaitN1            ; No
  864.     SUB BL,100            ; Yes.    Subtract 100 hundreths
  865.     INC BH                ; And add a second
  866. WaitN1: CMP BH,60            ; Wrap around check
  867.      JL WaitN2            ; No wrap
  868.     SUB BH,60            ; Forget about Days and Hours
  869. WaitN2: INT DOS             ; Get the time again
  870.     CMP DX,BX            ; Is current time after the deadline?
  871.      JB WaitN2            ; No, keep checking 'til time runs out
  872.     POP DX
  873.     POP CX
  874.     POP BX
  875.     POP CX
  876.     MOV SP,BP
  877.     POP BP
  878.     RET
  879. WaitN    ENDP
  880.     PAGE ;
  881. ;
  882. ; R_COUNT - RETURNS NUMBER OF BYTES IN THE RECEIVE BUFFER IN AX
  883. ;        total in DX
  884. ;
  885. _r_count    PROC FAR
  886.     push bp
  887.     mov bp,sp
  888.     push si
  889.     PUSHF
  890.     PUSH    SI
  891.     MOV    SI,CURRENT_AREA     ; SI POINTS TO DATA AREA
  892.     MOV    AX,0            ; NOTHING RECEIVED IF NOT INSTALLED
  893.     mov dx,R_SIZE
  894.     TEST    INSTALLED[SI],1     ; PORT INSTALLED?
  895.      JZ    RCX            ; ABORT IF NOT
  896.     MOV    AX,SIZE_RDATA[SI]    ; GET NUMBER OF BYTES USED
  897. RCX:    POP    SI
  898.     POPF
  899.     pop si
  900.     mov sp,bp
  901.     pop bp
  902.     RET
  903. _r_count    ENDP
  904.     PAGE ;
  905. ;
  906. ; RECEIVE - RETURNS THE NEXT CHARACTER FROM THE RECEIVE BUFFER IN AL
  907. ;        AND REMOVES IT FROM THE BUFFER
  908. ;        THE PARITY BIT IS STRIPPED OFF if    not in Parity mode
  909. ;    Returns AX: -1 if port not installed or no characters available
  910. ;        or AX: the next character
  911. ;
  912. _receive_com    PROC FAR
  913.     push bp
  914.     mov bp,sp
  915.     push si
  916.     PUSHF                ; SAVE FLAGS
  917.     PUSH    BX            ; SAVE REGS
  918.     PUSH    SI
  919.     MOV    SI,CURRENT_AREA     ; SI POINTS TO DATA AREA
  920.     mov    ax,-1            ; -1 if bad call
  921.     TEST    INSTALLED[SI],1     ; PORT INSTALLED?
  922.      JZ    RCVX            ; ABORT IF NOT
  923.     CMP    SIZE_RDATA[SI],0    ; ANY CHARACTERS?
  924.      JE    RCVX            ; ABORT IF NOT
  925.  
  926. ; GOOD CALL
  927.     mov ah,0            ; good call
  928.     MOV    BX,START_RDATA[SI]    ; GET POINTER TO OLDEST CHAR
  929.     MOV    AL,RDATA[SI][BX]    ; GET CHAR FROM BUFFER
  930.     CMP    PARITY[SI],'N'          ; ARE WE RUNNING WITH NO PARITY? LBA
  931.      JE    L11            ; IF SO, DON'T STRIP HIGH BIT    LBA
  932.     AND    AL,7FH            ; STRIP PARITY BIT
  933. L11:    INC    BX            ; BUMP START_RDATA
  934.     AND BX,R_SIZE-1         ; Ring the pointer
  935.     MOV    START_RDATA[SI],BX    ; SAVE THE NEW START_RDATA VALUE
  936.     DEC    SIZE_RDATA[SI]        ; ONE LESS CHARACTER
  937.     CMP    XON_XOFF[SI],'E'        ; FLOW CONTROL ENABLED?
  938.      JNE    RCVX            ; DO NOTHING IF DISABLED
  939.     CMP    HOST_OFF[SI],1        ; HOST TURNED OFF?
  940.      JNE    RCVX            ; JUMP IF NOT
  941.     CMP    SIZE_RDATA[SI],R_SIZE/16; RECEIVE BUFFER NEARLY EMPTY?
  942.      JGE    RCVX            ; DONE IF NOT
  943.     MOV    HOST_OFF[SI],0        ; TURN ON HOST IF SO
  944.  
  945.     PUSH    AX            ; SAVE RECEIVED CHAR
  946.     MOV    AL,CONTROL_Q        ; TELL HIM TO TALK
  947. LI3:    CLI                ; TURN OFF INTERRUPTS
  948.     CMP URGENT_SEND[SI],1        ; Previous send still in progress?
  949.      JNE LI4            ; No.  There is space now.
  950.     STI                ; Yes.    Wait for interrupt to take it.
  951.     JMP SHORT LI3            ; Loop 'til it's gone
  952. LI4:    CALL    SENDII            ; SEND IMMEDIATELY INTERNAL
  953.     STI                ; INTERRUPTS BACK ON
  954.     POP    AX            ; RESTORE RECEIVED CHAR
  955.  
  956. RCVX:    POP    SI            ; RECOVER REGS
  957.     POP    BX
  958.     POPF                ; RECOVER FLAGS
  959.     pop si
  960.     mov sp,bp
  961.     pop bp
  962.     RET
  963. _receive_com    ENDP
  964.     PAGE ;
  965. ;
  966. ; S_COUNT - RETURNS IN AX THE AMOUNT OF FREE SPACE
  967. ;        REMAINING IN THE TRANSMIT BUFFER
  968. ;    DX total size
  969. ; N.B. The free space might be negative (-1) if the buffer was full and then
  970. ; the program called SENDI or RX_INT required sending a control-S to squelch
  971. ; the remote sender.  Return 0 in this case.
  972. ;
  973. _s_count PROC FAR
  974.     push bp
  975.     mov bp,sp
  976.     push si
  977.     PUSHF
  978.     PUSH    SI
  979.     MOV    SI,CURRENT_AREA     ; SI POINTS TO DATA AREA
  980.     MOV    AX,0            ; NO SPACE LEFT IF NOT INSTALLED
  981.     mov dx,S_SIZE-1         ; Leave 1 byte for a SENDII call
  982.     TEST    INSTALLED[SI],1     ; PORT INSTALLED?
  983.      JZ    SCX            ; ABORT IF NOT
  984.     MOV AX,S_SIZE-1         ; GET THE SIZE OF THE X-MIT BUFFER
  985.     SUB AX,SIZE_TDATA[SI]        ; SUBTRACT THE NUMBER OF BYTES USED
  986.      JGE SCX            ; Avoid returning negative number
  987.     XOR AX,AX            ; Return 0
  988. SCX:    POP    SI            ; RECOVER SI
  989.     POPF                ; RESTORE FLAGS
  990.     pop si
  991.     mov sp,bp
  992.     pop bp
  993.     RET
  994. _s_count    ENDP
  995.     PAGE ;
  996. ;
  997. ; SEND - SEND A CHARACTER
  998. ;    [bp+6] = char
  999. ;
  1000. _send_com    PROC FAR
  1001.     push bp
  1002.     mov bp,sp
  1003.     push si
  1004.     PUSHF                ; SAVE FLAGS
  1005.     PUSH    AX            ; SAVE REGS
  1006.     PUSH    BX
  1007.     PUSH    DX
  1008.     PUSH    SI
  1009.     MOV    SI,CURRENT_AREA     ; SI POINTS TO DATA AREA
  1010.     TEST    INSTALLED[SI],1     ; PORT INSTALLED?
  1011.      JZ    L44            ; ABORT IF NOT
  1012.  
  1013. SENDC1: CMP    SIZE_TDATA[SI],S_SIZE-1 ; BUFFER FULL? (Leave room for SENDII)
  1014.      JGE SENDC1            ; Wait for interrupts to empty buffer
  1015.  
  1016. L4A:    MOV    BX,END_TDATA[SI]    ; BX POINTS TO FREE SPACE
  1017.     MOV AL,[BP+6]            ; Character to send
  1018.     MOV    TDATA[SI][BX],AL    ; MOVE CHAR TO BUFFER
  1019.     INC    BX            ; INCREMENT END_TDATA
  1020.     AND BX,S_SIZE-1         ; Ring the pointer
  1021.     MOV    END_TDATA[SI],BX    ; SAVE NEW END_TDATA
  1022.     INC    SIZE_TDATA[SI]        ; ONE MORE CHARACTER IN X-MIT BUFFER
  1023.     MOV    DX,IER[SI]        ; INTERRUPT ENABLE REGISTER
  1024.     IN    AL,DX            ; GET IT
  1025.     TEST    AL,2            ; SEE IF TX INTERRUPTS ARE ENABLED
  1026.      JNZ    L44            ; JUMP IF SO
  1027.     CMP SEND_OK[SI],1        ; See if Data Set Ready & CTS are on
  1028.      JNE L44            ; No. Still can't enable TX ints
  1029.     TEST PC_OFF[SI],1        ; Were we stopped by a ^S from host?
  1030.      JNZ L44            ; Yes.    Don't enable interrupts yet.
  1031.     MOV AL,0FH            ; IF NOT THEN RCV, TX, LINE & Modem
  1032.     OUT    DX,AL            ; ARE ENABLED.
  1033. L44:    POP    SI            ; RESTORE REGS
  1034.     POP    DX
  1035.     POP    BX
  1036.     POP    AX
  1037.     POPF                ; RESTORE FLAGS
  1038.     pop si
  1039.     mov sp,bp
  1040.     pop bp
  1041.     RET                ; DONE
  1042. _send_com    ENDP
  1043.     PAGE ;
  1044. ;
  1045. ; SENDI - SEND A CHARACTER IMMEDIATELY
  1046. ; [bp+6] = char to send
  1047. ;
  1048. _sendi_com    PROC FAR
  1049.     push bp
  1050.     mov bp,sp
  1051.     push si
  1052.     mov al,[bp+6]
  1053.     PUSHF                ; SAVE FLAGS
  1054.     PUSH    SI
  1055.     MOV    SI,CURRENT_AREA     ; SI POINTS TO DATA AREA
  1056.     TEST    INSTALLED[SI],1     ; PORT INSTALLED?
  1057.      JZ    LQ44            ; ABORT IF NOT
  1058.  
  1059. SENDI3: CLI                ; TURN OFF INTERRUPTS
  1060.     CMP URGENT_SEND[SI],1        ; Previous send still in progress?
  1061.      JNE SENDI4            ; No.  There is space now.
  1062.     STI                ; Yes.    Wait for interrupt to take it.
  1063.     JMP SHORT SENDI3        ; Loop 'til it's gone
  1064.  
  1065. SENDI4: CALL    SENDII            ; CALL INTERNAL SEND IMMEDIATE
  1066.     STI                ; INTERRRUPTS BACK ON
  1067.  
  1068. LQ44:    POP    SI            ; RESTORE REGS
  1069.     POPF                ; RESTORE FLAGS
  1070.     pop si
  1071.     mov sp,bp
  1072.     pop bp
  1073.     RET                ; DONE
  1074. _sendi_com    ENDP
  1075.     PAGE ;
  1076. ;
  1077. ; INTERNAL ROUTINE -- Called from process level and (receive) interrupt level
  1078. ; DEPENDS ON CALLER TO KEEP INTERRUPTS CLEARED AND SET SI
  1079. ; SENDI - SEND A CHARACTER IMMEDIATELY (PUT AT BEGINNING OF QUEUE)
  1080. ; AL = CHAR TO WRITE
  1081. ;
  1082. SENDII    PROC    NEAR
  1083.     PUSH BX
  1084.     PUSH    DX            ; SAVE DX
  1085.     CMP    SIZE_TDATA[SI],S_SIZE    ; BUFFER FULL?
  1086.      JB    SENDII2         ; JUMP IF NOT
  1087.     INC    WORD PTR EOVFLOW[SI]    ; BUMP ERROR COUNT (Can this happen?)
  1088.     MOV    BX,START_TDATA[SI]    ; BX POINTS TO FIRST CHAR IN BUFFER
  1089.     MOV    TDATA[SI][BX],AL    ; CLOBBER FIRST CHAR IN BUFFER
  1090.     JMP    SHORT SENDII4
  1091.  
  1092. SENDII2:MOV    BX,START_TDATA[SI]    ; BX POINTS TO FIRST CHAR IN BUFFER
  1093.     DEC    BX            ; BACKUP THE PTR
  1094.     CMP    BX,-1            ; BEFORE BEGINNING?
  1095.      JNE    SENDII3         ; JUMP IF NOT
  1096.     MOV    BX,S_SIZE-1        ; POINT TO END IF SO
  1097. SENDII3:MOV    TDATA[SI][BX],AL    ; MOVE CHAR TO BUFFER
  1098.     MOV    START_TDATA[SI],BX    ; SAVE NEW START_TDATA
  1099.     INC    SIZE_TDATA[SI]        ; ONE MORE CHARACTER IN X-MIT BUFFER
  1100.     MOV URGENT_SEND[SI],1        ; Flag high priority message
  1101. SENDII4:MOV    DX,IER[SI]        ; INTERRUPT ENABLE REGISTER
  1102.     IN    AL,DX            ; GET IT
  1103.     TEST    AL,2            ; SEE IF TX INTERRUPTS ARE ENABLED
  1104.      JNZ    SENDIIX         ; JUMP IF SO
  1105.     CMP SEND_OK[SI],1        ; See if Data Set Ready & CTS are on
  1106.      JNE SENDIIX            ; No. Still can't enable TX ints
  1107.     MOV AL,0FH            ; IF NOT THEN RCV, TX, LINE & Modem
  1108.     OUT    DX,AL            ; ARE ENABLED
  1109. SENDIIX:POP    DX            ; RECOVER DX
  1110.     POP BX
  1111.     RET                ; DONE
  1112. SENDII    ENDP
  1113.     PAGE ;
  1114. ;
  1115. ; S_LOCAL
  1116. ;
  1117. _send_local    PROC FAR
  1118.     push bp
  1119.     mov bp,sp
  1120.     push si
  1121.     PUSHF                ; SAVE FLAGS
  1122.     PUSH    AX            ; SAVE REGS
  1123.     PUSH    BX
  1124.     PUSH    SI
  1125.     MOV    SI,CURRENT_AREA     ; SI POINTS TO DATA AREA
  1126.     TEST    INSTALLED[SI],1     ; PORT INSTALLED?
  1127.      JZ    SLX            ; ABORT IF NOT
  1128.  
  1129.     CLI                ; INTERRUPTS OFF
  1130.     CMP    SIZE_RDATA[SI],R_SIZE    ; SEE IF ANY ROOM
  1131.      JB    L13A            ; SKIP IF ROOM
  1132.     INC    WORD PTR EOVFLOW[SI]    ; BUMP OVERFLOW COUNT
  1133.     JMP    SHORT L14        ; PUNT
  1134.  
  1135. L13A:    MOV    BX,END_RDATA[SI]    ; BX POINTS TO FREE SPACE
  1136.     MOV AL,[BP+6]            ; Get the byte to send
  1137.     MOV RDATA[SI][BX],AL        ; Put into buffer
  1138.     INC    BX            ; INCREMENT END_RDATA POINTER
  1139.     AND BX,R_SIZE-1         ; Ring the pointer
  1140.     MOV    END_RDATA[SI],BX    ; SAVE VALUE
  1141.     INC    SIZE_RDATA[SI]        ; GOT ONE MORE CHARACTER
  1142. L14:    STI                ; INTERRUPTS BACK ON
  1143.  
  1144. SLX:    POP    SI            ; RECOVER REGS
  1145.     POP    BX
  1146.     POP    AX
  1147.     POPF                ; RECOVER FLAGS
  1148.     pop si
  1149.     mov sp,bp
  1150.     pop bp
  1151.     RET                ; DONE
  1152. _send_local    ENDP
  1153.     PAGE ;
  1154. ;
  1155. ; BREAK - CAUSES A BREAK TO BE SENT OUT ON THE LINE
  1156. ;
  1157. _break_com    PROC FAR
  1158.     push bp
  1159.     mov bp,sp
  1160.     push si
  1161.     PUSHF
  1162.     PUSH    AX
  1163.     PUSH BX
  1164.     PUSH    CX
  1165.     PUSH    DX
  1166.     MOV    SI,CURRENT_AREA     ; SI POINTS TO DATA AREA
  1167.     TEST    INSTALLED[SI],1     ; PORT INSTALLED?
  1168.      JZ    BREAKX            ; ABORT IF NOT
  1169.  
  1170.     MOV    DX,LCR[SI]        ; LINE CONTROL REGISTER
  1171.     IN    AL,DX            ; GET CURRENT SETTING
  1172.     JMP    $+2            ; I/O DELAY FOR JR
  1173.     OR    AL,40H            ; TURN ON BREAK BIT
  1174.     OUT    DX,AL            ; SET IT ON THE UART
  1175.     MOV AX,25            ; 25/100 of a second
  1176.     CALL WaitN
  1177.     MOV    DX,LCR[SI]        ; LINE CONTROL REGISTER
  1178.     IN    AL,DX            ; GET CURRENT SETTING
  1179.     JMP    $+2            ; I/O DELAY FOR JR
  1180.     AND    AL,0BFH         ; TURN OFF BREAK BIT
  1181.     OUT    DX,AL            ; RESTORE LINE CONTROL REGISTER
  1182. BREAKX: POP    DX            ; RECOVER REGS
  1183.     POP    CX
  1184.     POP BX
  1185.     POP    AX
  1186.     POPF                ; RECOVER FLAGS
  1187.     pop si
  1188.     mov sp,bp
  1189.     pop bp
  1190.     RET                ; DONE
  1191. _break_com    ENDP
  1192.     PAGE ;
  1193.  
  1194. ; COM_ERRORS - RETURN POINTER IN dx:ax TO ERROR COUNTS
  1195. ;
  1196. _com_errors    PROC FAR
  1197.     push bp
  1198.     mov bp,sp
  1199.     mov ax,OFFSET DGROUP:CURRENT_AREA
  1200.     add ax,ERROR_BLOCK
  1201.     mov dx,ds
  1202.     mov sp,bp
  1203.     pop bp
  1204.     RET
  1205. _com_errors    ENDP
  1206.     PAGE ;
  1207. ;
  1208. ; _MODEM_STATUS - Returns the modem status register in AL
  1209. ;
  1210. ; Bits are:    0x80:    -CD    (Carrier Detect, inverted)
  1211. ;        0x40:    -RI    (Ring Indicator, inverted)
  1212. ;        0x20:    -DSR    (Data Set Ready, inverted)
  1213. ;        0x10:    -CTS    (Clear to Send, inverted)
  1214. ;        0x08:    Delta Carrier Detect    (CD changed)
  1215. ;        0x04:    Trailing edge of RI    (RI went OFF)
  1216. ;        0x02:    Delta DSR        (DSR changed)
  1217. ;        0x01:    Delta CTS        (CTS changed)
  1218. ;
  1219. _modem_status    PROC FAR
  1220.     push bp
  1221.     mov bp,sp
  1222.     PUSH DX
  1223.     PUSH SI
  1224.     MOV SI,CURRENT_AREA        ; Point to block for selected port
  1225.     MOV DX,MSR[SI]            ; IO Addr of Modem Status Register
  1226.     IN AL,DX            ; Get the live value
  1227.     XOR AH,AH            ; Flush unwanted bits
  1228.     POP SI
  1229.     POP DX
  1230.     mov sp,bp
  1231.     pop bp
  1232.     RET
  1233. _modem_status    ENDP
  1234.     PAGE ;
  1235. ;
  1236. ; INT_HNDLR1 - HANDLES INTERRUPTS GENERATED BY COM1:
  1237. ;
  1238. INT_HNDLR1 PROC FAR
  1239.     PUSH    SI
  1240.     MOV    SI,OFFSET DGROUP:AREA1    ; DATA AREA FOR COM1:
  1241.     JMP    SHORT INT_COMMON    ; CONTINUE
  1242. ;
  1243. ; INT_HNDLR2 - HANDLES INTERRUPTS GENERATED BY COM2:
  1244. ;
  1245. INT_HNDLR2 PROC FAR
  1246.     PUSH    SI
  1247.     MOV    SI,OFFSET DGROUP:AREA2    ; DATA AREA FOR COM2:
  1248.     JMP    SHORT INT_COMMON    ; CONTINUE
  1249. ;
  1250. ; INT_HNDLR3 - HANDLES INTERRUPTS GENERATED BY COM3:
  1251. ;
  1252. INT_HNDLR3 PROC FAR
  1253.     PUSH    SI
  1254.     MOV    SI,OFFSET DGROUP:AREA3    ; DATA AREA FOR COM3:
  1255.     JMP    SHORT INT_COMMON    ; CONTINUE
  1256. ;
  1257. ; INT_HNDLR4 - HANDLES INTERRUPTS GENERATED BY COM4:
  1258. ;
  1259. INT_HNDLR4 PROC FAR
  1260.     PUSH    SI
  1261.     MOV    SI,OFFSET DGROUP:AREA4    ; DATA AREA FOR COM4:
  1262.     ; Fall into INT_COMMON
  1263.     PAGE ;
  1264. ;
  1265. ; BODY OF INTERRUPT HANDLER
  1266. ;
  1267. INT_COMMON:
  1268.     PUSH    AX
  1269.     PUSH    BX
  1270.     PUSH    CX
  1271.     PUSH    DX
  1272.     PUSH    DS
  1273.  
  1274.     MOV AX,DGROUP            ; Offsets are relative to DGROUP [WWP]
  1275.     MOV    DS,AX
  1276.  
  1277. ; CLEAR THE INTERRUPT CONTROLLER FLAG
  1278.     MOV    DX,INTA00        ; 8259 CONTROL PORT
  1279.     MOV    AL,EOI[SI]        ; SPECIFIC END OF INTERRUPT
  1280.     OUT    DX,AL            ; CLEAR FLAG
  1281.  
  1282. ; FIND OUT WHERE INTERRUPT CAME FROM AND JUMP TO ROUTINE TO HANDLE IT
  1283. REPOLL: MOV DX,IIR[SI]            ; Interrupt Identification Register
  1284.     IN AL,DX
  1285.     TEST AL,1            ; Check the "no interrupt present" bit
  1286.      JNZ INT_END            ; ON means we are done
  1287.     MOV BL,AL            ; Put where we can index by it
  1288.     AND BX,000EH            ; Ignore FIFO_ENABLED bits, etc.
  1289.     JMP WORD PTR CS:INT_DISPATCH[BX]; Go to appropriate routine
  1290.  
  1291. INT_DISPATCH:
  1292.     DW MSTAT_INT        ; 0: Modem status interrupt
  1293.     DW TX_INT        ; 2: Transmitter interrupt
  1294.     DW RX_INT        ; 4: Receiver interrupt
  1295.     DW LSTAT_INT        ; 6: Line status interrupt
  1296.     DW REPOLL        ; 8: (Future use by UART makers)
  1297.     DW REPOLL        ; A: (Future use by UART makers)
  1298.     DW RX_INT        ; C: FIFO Timeout. Drain data trapped in FIFO.
  1299.     DW REPOLL        ; E: (Future use by UART makers)
  1300.  
  1301. INT_END:POP    DS
  1302.     POP    DX
  1303.     POP    CX
  1304.     POP    BX
  1305.     POP    AX
  1306.  
  1307.     POP    SI
  1308.     IRET
  1309.     PAGE ;
  1310. ;
  1311. ; Line status interrupt
  1312. ;
  1313. LSTAT_INT:
  1314.     MOV DX,LSR[SI]            ; Line status register
  1315.     IN AL,DX            ; Read line status & bump error counts
  1316.     TEST    AL,2            ; OVERRUN ERROR?
  1317.      JZ    LSI1            ; JUMP IF NOT
  1318.     INC    WORD PTR EOVRUN[SI]    ; ELSE BUMP ERROR COUNT
  1319. LSI1:    TEST    AL,4            ; PARITY ERROR?
  1320.      JZ    LSI2            ; JUMP IF NOT
  1321.     INC    WORD PTR EPARITY[SI]    ; ELSE BUMP ERROR COUNT
  1322. LSI2:    TEST    AL,8            ; FRAMING ERROR?
  1323.      JZ    LSI3            ; JUMP IF NOT
  1324.     INC    WORD PTR EFRAME[SI]    ; ELSE BUMP ERROR COUNT
  1325. LSI3:    TEST    AL,16            ; BREAK RECEIVED?
  1326.      JZ    LSI4            ; JUMP IF NOT
  1327.     INC    WORD PTR EBREAK[SI]    ; ELSE BUMP ERROR COUNT
  1328. LSI4:    JMP    REPOLL            ; SEE IF ANY MORE INTERRUPTS
  1329.  
  1330. ;
  1331. ; Modem status interrupt
  1332. ;
  1333. MSTAT_INT:
  1334.     MOV DX,MSR[SI]            ; Modem Status Register
  1335.     IN AL,DX            ; Read status & clear interrupt
  1336.     CMP CONNECTION[SI],'D'          ; Direct connection - ignore int
  1337.      JE MSI0            ; Just noise on DSR,CTS pins
  1338.     AND AL,30H            ; Expose CTS and Data Set Ready
  1339.     CMP AL,30H            ; Both on?
  1340.      JE MSI0            ; Yes.    Enable output at TX_INT
  1341.     XOR AL,AL
  1342.     JMP SHORT MSI1
  1343.  
  1344. MSI0:    MOV AL,1
  1345. MSI1:    MOV SEND_OK[SI],AL        ; Put where TXI and send_com can see
  1346.     MOV DX,IER[SI]            ; Let a TX int happen for thoro chks
  1347.     MOV AL,0FH            ; Line & modem sts, recv, send.
  1348.     OUT DX,AL
  1349.     JMP REPOLL            ; Check for other interrupts
  1350.     PAGE ;
  1351. ;
  1352. ; Tranmit interrupt
  1353. ;
  1354. TX_INT: CMP SEND_OK[SI],1        ; Harware (CTS & DSR on) OK?
  1355.      JNE TXI9            ; No.  Must wait 'til cable right!
  1356.     MOV CX,1            ; Transfer count for flow ctl
  1357.     CMP URGENT_SEND[SI],1        ; Flow control character to send?
  1358.      JE TXI3            ; Yes.    Always send flow control.
  1359.     CMP PC_OFF[SI],1        ; Flow control (XON/XOFF) OK?
  1360.      JE TXI9            ; Stifled & not urgent. Forget it.
  1361.  
  1362. TXI1:    MOV CL,UART_SILO_LEN[SI]    ; MAX size chunk (1 for simple 8250)
  1363.     ; Too bad there is no "Tranmitter FIFO Full" indication!
  1364.     CMP SIZE_TDATA[SI],CX        ; SEE IF ANY MORE DATA TO SEND
  1365.      JG TXI2            ; UART is the limit
  1366.     MOV CX,SIZE_TDATA[SI]        ; Buffer space limited.  Use that.
  1367. TXI2:     JCXZ TXI9            ; No data, disable TX ints
  1368.  
  1369. TXI3:    MOV    BX,START_TDATA[SI]    ; BX POINTS TO NEXT CHAR. TO BE SENT
  1370.     MOV    AL,TDATA[SI][BX]    ; GET DATA FROM BUFFER
  1371.     MOV    DX,DATREG[SI]        ; DX EQUALS PORT TO SEND DATA TO
  1372.     OUT    DX,AL            ; SEND DATA
  1373.     INC    BX            ; INCREMENT START_TDATA
  1374.     AND BX,S_SIZE-1         ; Ring the pointer
  1375.     MOV    START_TDATA[SI],BX    ; SAVE START_TDATA
  1376.     DEC    SIZE_TDATA[SI]        ; ONE LESS CHARACTER IN X-MIT BUFFER
  1377.      LOOP TXI3            ; Keep going 'til silo is full
  1378.     MOV URGENT_SEND[SI],0        ; Tell process level we sent flow ctl
  1379.     JMP    REPOLL
  1380.  
  1381. ; IF NO DATA TO SEND, or can't send, RESET TX INTERRUPT AND RETURN
  1382. TXI9:    MOV DX,IER[SI]
  1383.     MOV AL,0DH            ; Line & modem sts, recv, no send.
  1384.     OUT DX,AL
  1385.     JMP REPOLL
  1386.     PAGE ;
  1387. ;
  1388. ; Receive interrupt
  1389. ;
  1390. RX_INT:
  1391. RXI0:    MOV DX,LSR[SI]            ; Line Status Register
  1392.     IN AL,DX            ; Read it
  1393.     TEST AL,1            ; Check the RECV DATA READY bit
  1394.      JE RXIX            ; No more data available
  1395.     MOV    DX,DATREG[SI]        ; UART DATA REGISTER
  1396.     IN AL,DX            ; Get data, clear status
  1397.     CMP    XON_XOFF[SI],'E'        ; FLOW CONTROL ENABLED?
  1398.      JNE RXI2            ; No.  Don't check for XON/XOFF
  1399.  
  1400. ; Check each character for possible flow control (XON/XOFF)
  1401.     AND    AL,7FH            ; STRIP PARITY
  1402.     CMP    AL,CONTROL_S        ; STOP COMMAND RECEIVED?
  1403.      JNE RXI1            ; Jump if not. Might be ^Q though.
  1404.     MOV PC_OFF[SI],1        ; Stop output
  1405.     JMP SHORT RXI0            ; Don't store character
  1406.  
  1407. RXI1:    CMP    AL,CONTROL_Q        ; GO COMMAND RECEIVED?
  1408.      JNE RXI2            ; No.  Not a flow control character
  1409.     MOV PC_OFF[SI],0        ; Enable output
  1410.     JMP SHORT RXI0            ; Don't store character
  1411.  
  1412. ; Have a real data byte.  Store if possible.
  1413. RXI2:    CMP    SIZE_RDATA[SI],R_SIZE    ; SEE IF ANY ROOM
  1414.      JL RXI6            ; CONTINUE IF SO
  1415.     INC    WORD PTR EOVFLOW[SI]    ; BUMP OVERFLOW ERROR COUNT
  1416.     JMP SHORT RXIX
  1417.  
  1418. RXI6:    MOV    BX,END_RDATA[SI]    ; BX POINTS TO FREE SPACE
  1419.     MOV    RDATA[SI][BX],AL    ; MOVE DATA TO BUFFER
  1420.     INC    SIZE_RDATA[SI]        ; GOT ONE MORE CHARACTER
  1421.     INC    BX            ; INCREMENT END_RDATA POINTER
  1422.     AND BX,R_SIZE-1         ; Ring the pointer
  1423.     MOV    END_RDATA[SI],BX    ; SAVE VALUE
  1424.  
  1425. ; See if we must tell remote host to stop outputting.
  1426.     CMP    XON_XOFF[SI],'E'        ; FLOW CONTROL ENABLED?
  1427.      JNE RXIX            ; No
  1428.     CMP HOST_OFF[SI],1        ; Already told remote to shut up?
  1429.      JE RXIX            ; Yes.    Don't flood him with ^Ss
  1430.     CMP    SIZE_RDATA[SI],R_SIZE/2 ; RECEIVE BUFFER NEARLY FULL?
  1431.      JLE RXIX            ; No.  No need to stifle remote end
  1432.     ; Would like to wait here for URGENT_SEND to go off if it is on.
  1433.     ; But we need to take a TX interrupt for that to happen.
  1434.     MOV    AL,CONTROL_S        ; TURN OFF HOST IF SO
  1435.     CALL    SENDII            ; SEND IMMEDIATELY INTERNAL
  1436.     MOV    HOST_OFF[SI],1        ; HOST IS NOW OFF
  1437. RXIX:    JMP    REPOLL
  1438.  
  1439. INT_HNDLR4 ENDP
  1440. INT_HNDLR3 ENDP
  1441. INT_HNDLR2 ENDP
  1442. INT_HNDLR1 ENDP
  1443. COM_TEXT    ENDS
  1444.     END
  1445.